import os
from jsonschema.exceptions import ValidationError
from prometheus_client import start_http_server, CollectorRegistry, multiprocess
import time
import uuid
from flask import Flask, g, request
from common.decorators.request_body import request_body
from common.decorators.response_body import response_body
from common.exception.base_exception import BaseBizException
from common.exception.biz_exception import BizException
from common.reponse.result import Result
from common.exception.biz_error_type import BizErrorType
from monitor.metric import Metric
from common.utils.log_utils import LogUtils
from multiprocessing import Lock, Value

lock = Lock()
worker_cnt = Value('i', 0)


def start_monitor():
    with lock:
        try:
            registry = CollectorRegistry()
            multiprocess.MultiProcessCollector(registry)
            start_http_server(16000 + worker_cnt.value, registry=registry)
            LogUtils.info(
                f'Prometheus client start success,pid is {os.getpid()},port is {str(16000 + worker_cnt.value)}')
            worker_cnt.value += 1
        except:
            pass


def create_app():
    start_monitor()
    app = Flask(__name__)

    @app.before_request
    def before_request():
        trace_id = request.headers.get('traceId')
        if trace_id is None:
            trace_id = str(uuid.uuid4())
        g.trace_id = trace_id
        app.logger.info(f'[{request.path}, Pid is {os.getpid()}], request data is [{request.get_data()}]')
        g.start_time = time.time()

    @app.after_request
    def after_request(response):
        data = response.get_json()
        code = 0
        if data is not None:
            code = data['code']
        Metric.REQUEST_TOTAL.labels(uri=request.path, code=code).inc()
        duration = time.time() - g.start_time

        Metric.REQUEST_DURATION.labels(uri=request.path, code=code).observe(duration)
        if response.is_json:
            app.logger.info(f'[{request.path}, response data is [{response.get_json()}]')
        return response

    @app.errorhandler(ValidationError)
    @response_body
    def error_handler(e):
        app.logger.error(f'[an validation exception has occurred. [{e}]')
        return Result.fail_with_custom_code(BizErrorType.PARAM_ILLEGAL.code, f'{e.message},jsonpath is [{e.json_path}]')

    @app.errorhandler(BaseBizException)
    @response_body
    def error_handler(e):
        app.logger.exception(f'[an exception has occurred. [{e}]')
        return Result.fail_with_base_exception(e)

    @app.errorhandler(Exception)
    @response_body
    def error_handler(e):
        app.logger.exception(f'[an exception has occurred. [{e}]')
        if hasattr(e, 'code') and hasattr(e, 'name'):
            code = e.code
            msg = e.name
            if (code is not None) and (msg is not None):
                return Result.fail_with_custom_code(code, msg)

        return Result.fail_with_error_type(BizErrorType.SYSTEM_ERROR)

    from .views import health_bp
    app.register_blueprint(health_bp)

    return app
